package org.ucas.kgraph.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.ucas.kgraph.dao.RelationDao;
import org.ucas.kgraph.dao.TermDao;
import org.ucas.kgraph.dto.RelationDto;
import org.ucas.kgraph.entity.Relation;
import org.ucas.kgraph.entity.RelationType;
import org.ucas.kgraph.entity.Term;
import org.ucas.kgraph.utils.exception.ConflictException;
import org.ucas.kgraph.utils.exception.ForbiddenException;
import org.ucas.kgraph.utils.exception.NotFoundException;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 负责关系的处理和查找
 *
 * @author suemi
 */
@Service
@Transactional
public class RelationService {

  @Autowired
  private RelationDao relationDao;

  @Autowired
  private TermDao termDao;

  public void ensure(Relation r){
    Term left = termDao.findOne(r.getLeftEnd());
    Term right = termDao.findOne(r.getRightEnd());
    if(left == null || right == null) throw NotFoundException.Term_NotExists;

    Long leftRoot = left.getRootId();
    Long rightRoot = right.getRootId();

    //System.out.println("------------------------------------qewwwwwwwwww"+leftRoot.toString());
   // System.out.println("------------------------------------qewwwwwwwwww"+rightRoot.toString());
    if(leftRoot<0 && rightRoot<0){ //不同顶级主题相连
      throw ForbiddenException.DIFFERENT_ROOTID;
    }

    if(leftRoot <0){ //不允许和其他顶级主题相连
      if(left.getId()!=rightRoot) throw ForbiddenException.DIFFERENT_ROOTID;

    }else if(rightRoot<0){
     // System.out.println("qweqqqqqqqqqqqqqqqqq"+right.getId());
      if(right.getId().longValue()!=leftRoot.longValue()) throw ForbiddenException.DIFFERENT_ROOTID;

    }else if(leftRoot.longValue()!=rightRoot.longValue()){ //不允许不同顶级主题下的节点相连
      throw ForbiddenException.DIFFERENT_ROOTID;
    }
    r.setLevel(Math.max(left.getLevel(),right.getLevel()));

  }

  public Relation save(Relation r) {
    return relationDao.save(r);
  }

  public Relation create(Relation r){
    Relation tmp = relationDao.findByCombination(r.getLeftEnd(),r.getRightEnd(),r.getCategory());
    if(tmp!=null) throw ConflictException.Relation_Confilict;

    if(RelationType.unDirected(r.getCategory())){
      // 对于关联连接，还需要检查left和right对调后的情况
      tmp = relationDao.findByCombination(r.getRightEnd(),r.getLeftEnd(),r.getCategory());
      if(tmp!=null) throw ConflictException.Relation_Confilict;
    }
    ensure(r);
    if(r.getCategory().equals(RelationType.Parent)){
      Term child = termDao.findOne(r.getLeftEnd());
      Term parent = termDao.findOne(r.getRightEnd());
      if(parent.getLevel()+1<child.getLevel() || child.getLevel()<0){
        child.setLevel(parent.getLevel()+1);
        r.setLevel(child.getLevel());
      }
      termDao.save(child);
    }
    return relationDao.save(r);
  }

  public Relation load(Long id) {
    return relationDao.findOne(id);
  }

  /**
   * @param term
   * @param oldParentId
   * @return 推导删除父子关系后子节点的层级
   */
  private Integer inferTermLevel(Term term, Long oldParentId){
    List<Long> parentIds = relationDao.parentIds(term.getId());
    parentIds.remove(oldParentId);

    if(parentIds.size()==0) return -1; // 除去旧父亲外没有父亲时直接成为孤立节点

    List<Term> parents = termDao.loadBatch(parentIds);
    return parents.stream()
            .map(t -> t.getLevel()).min(new Comparator<Integer>() {
      @Override
      public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1,o2);
      }
    }).orElse(-1);

  }

  public void delete(Long id) {
    Relation r =load(id);

    relationDao.delete(id);
    if(r.getCategory().equals(RelationType.Parent)){
      Term child = termDao.findOne(r.getLeftEnd());
      Term parent = termDao.findOne(r.getRightEnd());
      if(child.getLevel() == parent.getLevel()+1){
        Integer level = inferTermLevel(child,parent.getId());
        child.setLevel(level);
        termDao.save(child);
      }

    }
  }
  /*
  public Relation rename(Long id,String newName) {
	    Relation relation =load(id);
	    relation.setName(newName);
	    return relationDao.save(relation);
	  }
	  */
  public Relation rename(Relation r){
	 
	  Relation relation = relationDao.findByCombination(r.getLeftEnd(),r.getRightEnd(),r.getCategory());
	  if(relation==null && RelationType.unDirected(r.getCategory())){
	        relation = relationDao.findByCombination(r.getRightEnd(),r.getLeftEnd(),r.getCategory());
	  }
	  if(relation!=null){
	      relation.setName(r.getName());
	      return relationDao.save(relation);
		  //return rename(relation.getId(),r.getName());
	  }else{
		  throw ConflictException.Relation_Confilict;  
	  }
  }
  public void delete(Relation r){
    if(r.getId()!=null) delete(r.getId());
    else{
      Relation relation = relationDao.findByCombination(r.getLeftEnd(),r.getRightEnd(),r.getCategory());
      if(relation==null && RelationType.unDirected(r.getCategory())){
        relation = relationDao.findByCombination(r.getRightEnd(),r.getLeftEnd(),r.getCategory());
      }
      if(relation!=null)
        delete(relation.getId());
    }
  }

  public Relation safeSave(Relation r){
    ensure(r);
    return relationDao.save(r);
  }

  /**
   * 根据顶级主题过滤关系，不推荐使用
   * @param relations
   * @param rootId
   * @return
   */
  private List<Relation> filterByRoot(List<Relation> relations,Long rootId){
    if(rootId==null) return relations;
    return relations.stream().filter(r -> {
      Term left = termDao.findOne(r.getLeftEnd());
      Term right = termDao.findOne(r.getRightEnd());
      if(rootId<0) return left.getRootId()<0 && right.getRootId()<0;
      return (left.getRootId()<0?left.getId().equals(rootId):left.getRootId().equals(rootId)) &&
              (right.getRootId()<0?right.getId().equals(rootId):right.getRootId().equals(rootId));
    }).collect(Collectors.toList());
  }

  /**
   * @param termId
   * @param rootId
   * @return 在特定顶级主题下词条所有的关联关系
   */
  public List<Relation> links(Long termId, Long rootId){
    //目前默认关联只发生在同一顶级主题下，过滤暂不开启
//    return filterByRoot(relationDao.links(termId),rootId);
    return relationDao.links(termId);
  }

  public Relation reparent(Long termId,Long oldParent, Long newParent){
    Relation relation = new Relation();
    relation.setLeftEnd(termId);
    relation.setRightEnd(oldParent);
    relation.setCategory(RelationType.Parent);
    delete(relation);
    relation = create(new Relation(termId,newParent,RelationType.Parent));
    return relation;
  }


}
